import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;

public class ActionManager extends Thread

{
	private GuiManager GM;
	private ArrayList<Message> msgQueue;
	private boolean active;

	public void init()
	{
		GM=GuiManager.getInstance();
		msgQueue=new ArrayList<Message>();
		active=false;
	}

	@Override
	public void run()
	{
		Message msg;
		while (params.isActive)
		{
			while (!msgQueue.isEmpty())
			{
				msg=msgQueue.remove(0);
				handleIncomingMessage(msg);
				try {
					Thread.sleep(10);
				} catch (Exception e) {
					Log.writeEx(e);
				}
			}
			active=false;
			try {
				Thread.sleep(100);
			} catch (InterruptedException e)
			{
				Log.writeEx(e);
			}
		}
	}

	private void sendMessage(Message send)
	{
		GM.pushMessage(send);
	}

	private void handleIncomingMessage(Message msg)
	{
		Log.debug("Msg recieved: "+msg.getSubject().toString());
		write("---------------"+msg.getSubject().toString()+"-------------------");
		boolean ret=false;
		if (msg.getSubject()==MsgCommands.DEODEX)
			ret=deodexRom();
		else if (msg.getSubject()==MsgCommands.ADDROOT)
			ret=root();
		else if (msg.getSubject()==MsgCommands.ADBWIFI)
			ret=adbWifi();
		else if (msg.getSubject()==MsgCommands.PATCH_HEBREW)
			ret=rtl();
		else if (msg.getSubject()==MsgCommands.HOSTS)
			ret=hosts();
		else if (msg.getSubject()==MsgCommands.APN)
			ret=apn();
		else if (msg.getSubject()==MsgCommands.ANDROID_LOCK_SCREEN)
			ret=setLockScreen(MsgCommands.ANDROID_LOCK_SCREEN);
		else if (msg.getSubject()==MsgCommands.PUZZLE_LOCK_SCREEN)
			ret=setLockScreen(MsgCommands.PUZZLE_LOCK_SCREEN);
		else if (msg.getSubject()==MsgCommands.RINGER)
			ret=phone();
		else if (msg.getSubject()==MsgCommands.MMS)
			ret=sms();
		else if (msg.getSubject()==MsgCommands.CAMERA)
			ret=camera();
		else if (msg.getSubject()==MsgCommands.INTERNET)
			ret=internet();
		else if (msg.getSubject()==MsgCommands.CRT)
			ret=crt();
		else if (msg.getSubject()==MsgCommands.CONTACTS_RULER)
			ret=contacts(msg.getBody());
		else if (msg.getSubject()==MsgCommands.MUSIC_RULER)
			ret=music(msg.getBody());
		else if (msg.getSubject()==MsgCommands.COPY_TRANSLATION)
			ret=transltaions();
		else if (msg.getSubject()==MsgCommands.COPY_PREPARED_TRANSLATION)
			ret=preparedTranslations();
		else if (msg.getSubject()==MsgCommands.THEME)
			ret=theme(msg.getBody());
		else if (msg.getSubject()==MsgCommands.ADD_APPLICATIONS)
			ret=addapps();
		else if (msg.getSubject()==MsgCommands.REMOVE_APPLICATIONS)
			ret=removeapp(msg.getBody());
		else if (msg.getSubject()==MsgCommands.EDIT_ROM_NAME)
			ret=romName();
		else if (msg.getSubject()==MsgCommands.PACK_ROM)
			ret=packrom();
		else if (msg.getSubject()==MsgCommands.COMPILE)
			ret=compile(msg.getBody());
		else if (msg.getSubject()==MsgCommands.DECOMPILE)
			ret=decompile(msg.getBody());
		else if (msg.getSubject()==MsgCommands.EXTRACT)
			ret=extractRom(msg.getBody());
		else if (msg.getSubject()==MsgCommands.INIT)
			ret=initProject();
		else if (msg.getSubject()==MsgCommands.CLEAR_TEMP_FILES)
			ret=clearTempFiles();
		else if (msg.getSubject()==MsgCommands.TEST)
			ret=test();
		else 
			Log.error("Unknown message command recieved in action manager. Cmd: "+msg.getSubject().toString());
		if (ret)
			write("Finished succsefully");
		else
			write("Operation failed, please see log file");
	}

	private boolean crt()
	{
		AndroidFile and = new AndroidFile(params.activeProject+File.separator+"system"+File.separator+"framework"+File.separator+"framework-res.apk");
		if (!and.exists())
		{
			Log.debug(and.getAbsolutePath()+" was not found");
			return false;
		}
		write("decompiling apk");
		if (!and.decompile())
		{
			Log.debug("decompile of "+and.getAbsolutePath()+" failed");
			return false;
		}
		File patch=new File(and.getDecompiledPath(),"res"+File.separator+"values"+File.separator+"bools.xml");
		if (!patch.exists())
		{
			Log.error("could not find file to patch. cancel");
			and.clear();
			return false;
		}
		write("Patching file");
		if (StringProcess.getInstance().changeTagValue(patch.getAbsolutePath(), ".*config_animateScreenLights.*", "false")<=0)
		{
				Log.error("could not patch file");
				and.clear();
				return false;
		}
		write("compiling back");
		if (and.compile()==null)
		{
			and.clear();
			return false;
		}
		ArrayList<String> resources=new ArrayList<String>();
		write("pushing resources");
		resources.add("Resources.arsc");
		if (!and.replaceResources(resources))
		{
			and.clear();
			return false;
		}
		and.clear();
		return true;
	}

	private boolean setLockScreen(MsgCommands LockScreen)
	{
		AndroidFile and=new AndroidFile(params.activeProject+File.separator+"system"+File.separator+"framework"+File.separator+"android.policy.jar");
		if (!and.exists())
		{
			Log.debug(and.getAbsolutePath()+" was not found");
			return false;
		}
		write("Decompiling");
		and.decompile();
		if (!and.decompile())
		{
			Log.debug("decompile of "+and.getAbsolutePath()+" failed");
			return false;
		}
		
		write("patching..");
		File patchFile=new File(and.getDecompiledPath()+File.separator+"smali"+File.separator+"com"+File.separator+"android"+File.separator+"internal"+File.separator+"policy"+File.separator+"impl"+File.separator+"LockPatternKeyguardView.smali");
		if (!patchFile.exists())
		{
			Log.error("could not find file to patch. cancel");
			and.clear();
			return false;
		}
		String linePattern=null;
		String replace=null;
		String newValue=null;
		if (LockScreen==MsgCommands.ANDROID_LOCK_SCREEN)
		{
			linePattern="900";
			replace="->.*?:";
			newValue="->TapLock:";
		}
		else if (LockScreen==MsgCommands.PUZZLE_LOCK_SCREEN)
		{
			linePattern="900";
			replace="->.*?:";
			newValue="->PuzzleLock:";
		}
		else
		{
			Log.error("Unsupported lock screen command");
			return false;
		}
		
		if(StringProcess.getInstance().replaceAllInLine(patchFile.getAbsolutePath(), linePattern, replace, newValue)<=0)
		{
			Log.error("could not patch file");
			and.clear();
			return false;
		}
		
		if (LockScreen==MsgCommands.ANDROID_LOCK_SCREEN)
		{
			linePattern="907";
			replace="->.*?:";
			newValue="->TapLock:";
		}
		else if (LockScreen==MsgCommands.PUZZLE_LOCK_SCREEN)
		{
			linePattern="907";
			replace="->.*?:";
			newValue="->GlassLock:";
		}
		else
		{
			Log.error("Unsupported lock screen command");
			return false;
		}
		
		if(StringProcess.getInstance().replaceAllInLine(patchFile.getAbsolutePath(), linePattern, replace, newValue)<=0)
		{
			Log.error("could not patch file");
			and.clear();
			return false;
		}
		
		write("compiling back");
		if (and.compile()==null)
		{
			and.clear();
			return false;
		}
		ArrayList<String> resources=new ArrayList<String>();
		resources.add("classes.dex");
		if (!and.replaceResources(resources))
		{
			and.clear();
			return false;
		}
		and.clear();
		write("Patched lock screen successfully");
		
		return true;
	}

	private boolean internet()
	{
		write("Patching user-agent as a PC");
		boolean flag=true;
		AndroidFile and= new AndroidFile(params.activeProject+File.separator+"system"+File.separator+"framework"+File.separator+"framework-res.apk");
		if (!and.exists())
		{
			Log.debug(and.getAbsolutePath()+" was not found");
			return false;
		}
		write("Decompiling");
		if (!and.decompile())
		{
			Log.debug("decompile of "+and.getAbsolutePath()+" failed");
			return false;
		}
		write("patching..");
		String linePattern=".*web_user_agent.*";
		String newValue="Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Safari/530.17";
		File d=new File(and.getDecompiledPath()+File.separator+"res"+File.separator+"values"+File.separator+"strings.xml");
		if (!d.exists())
		{
			Log.error("could not find file to patch. cancel");
			and.clear();
			return false;
		}
		if(StringProcess.getInstance().changeTagValue(and.getDecompiledPath()+File.separator+"res"+File.separator+"values"+File.separator+"strings.xml", linePattern, newValue)<0)
		{
			Log.error("could not patch file");
			and.clear();
			return false;
		}
		write("compiling back");
		if (and.compile()==null)
		{
			and.clear();
			return false;
		}
		ArrayList<String> resources=new ArrayList<String>();
		resources.add("resources.arsc");
		if (!and.replaceResources(resources))
		{
			and.clear();
			return false;
		}
		and.clear();
		write("Patched user-agent successfully");
		return flag;
	}
	
	private boolean adbWifi()
	{
		File file=new File(params.activeProject+File.separator+"system"+File.separator+"etc"+File.separator+"init.d");
		file.mkdirs();
		file=new File(file,"99adbwifi");
		TextFileHandler tx=new TextFileHandler(file.getAbsolutePath(), true);
		try {
			tx.write("setprop service.adb.tcp.port 5555");
			tx.write("stop adbd");
			tx.write("start adbd");
			tx.close();
		} catch (IOException e) {
			Log.writeEx(e);
			return false;
		}
		return true;
	}

	private boolean packrom()
	{
		write("Clearing Temp Files");
		clearTempFiles();
		write("Zipaligning rom:");
		zipAlign();
		String newRomName="update.zip";
		File newRom=new File(newRomName);
		if (newRom.exists())
			newRom.delete();

		File temp=new File(params.activeProject);
		write("adding "+temp.getAbsolutePath()+" to rom file. this could take a while");
		if (!HelpFunctions.addToZip(newRomName, temp.getAbsolutePath()+File.separator+"*"))
			return false;
		write("finished compressing");


		temp=new File(newRomName+params.tempTag);
		write("signing new Rom");
		ArrayList<String> cmd=new ArrayList<String>(Arrays.asList(params.testsign));
		cmd.add(newRomName);
		cmd.add(temp.getAbsolutePath());
		HelpFunctions.run_command(cmd);
		if (!newRom.delete())
		{
			Log.error("could not delete update.zip after signing it");
			return false;
		}

		File signed=new File(newRom+params.tempTag);
		if (!signed.renameTo(newRom))
		{
			Log.error("could not rename "+signed.getAbsolutePath()+" to "+newRom.getAbsolutePath()+" after signing");
			return false;
		}
		write("finished.\nRom is ready in folder: "+System.getProperty("user.dir"));

		return true;
	}

	private boolean romName()
	{
		Log.debug("Change rom name: ");
		String fName="build.prop";
		File temp=new File(params.activeProject+File.separator+"system"+File.separator+fName);
		if (!temp.exists())
		{
			Log.debug("build.prop file could not be found under: "+temp.getAbsolutePath());
			return false;
		}
		String newName=JOptionPane.showInputDialog("Enter new Rom Name");
		if (newName==null)
		{
			write("name invalid. nothing changed");
			return true;
		}
		Log.debug("Rom Name="+newName);
		String fieldName="ro.build.display.id";
		int ret=StringProcess.getInstance().changeNonQuotedFieldValue(temp.getAbsolutePath(), fieldName, newName);
		if (ret==1)
			write("name changed succsefully");
		else
		{
			Log.error("change of rom name failed");
			write("could not change rom name. please check file manually");
		}
		return (ret>0);
	}

	private boolean removeapp(String list)
	{
		boolean flag=true;
		write("deleting selected files");
		String[] values=list.split(";");
		for (String fName : values)
		{
			File temp=new File(params.activeProject+File.separator+"system"+File.separator+"app"+File.separator+fName);
			if (!temp.exists())
			{
				write("could not find file "+temp.getAbsolutePath());
				Log.error("could not find file "+temp.getAbsolutePath());
				flag=false;
				continue;
			}
			else
			{
				Log.debug("deleting: "+fName);
				write("Deleting file: "+fName);
				temp.delete();
			}
		}
		write("finished!");
		return flag;
	}

	private boolean addapps() {
		boolean flag=false;
		JFileChooser chooser=new JFileChooser();
		chooser.setFileFilter(new APKFilter());
		chooser.setDialogTitle("Application Browser");
		chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		chooser.setMultiSelectionEnabled(true);
		chooser.showDialog(null, "OK");
		File[] apps=chooser.getSelectedFiles();
		if (apps==null)
		{
			write("no file selected");
			Log.debug("No file selected");
		}
		for (File a:apps)
		{
			if (!addApplication(a))
				flag=true;
		}
		if (flag)
			write("Error occoured. please see log file");
		write("finished");
		return !flag;

	}

	/**
	 * This function add a prepared theme to an android file.
	 * You need to prepare the tdirectory structure and files in the theme directory under lib folder in advance.
	 * @return
	 */
	private boolean theme(String file) 
	{
//		The logic used is as follows:
//		First decomile the apk
//		Then copy into it the prepared directory tree
//		Then compile back
//		and push into original apk resources.arsc/classes.dex if neccessery and every XML from the compiled apk
//		and push png files from theme directory
		//Get zip file containing theme
		
		
		AndroidFile and=new AndroidFile(file);
		
		write("Decompiling source file");
		if (!and.decompile())
		{
			Log.error("failed to decompile: "+and.getAbsolutePath());
			return false;
		}

		String decompiledpath=and.getDecompiledPath();
		try {
			HelpFunctions.copyDirectory(new File(params.themeDir), new File(decompiledpath));
		} catch (IOException e) {
			Log.writeEx(e);
		}
		
		write("compiling");
		if (and.compile()==null)
		{
			Log.error("could not compile patched theme file");
			write("failed to compile");
			return false;
		}
		
		//now update original apk
		ArrayList<String> resources=new ArrayList<String>(); //get list of files
		HelpFunctions.listFolderContent(new File(params.themeDir),"",resources);		
		resources.add("resources.arsc");
		File smaliTst=new File(params.themeDir+"smali");
		if (smaliTst.exists()) //if this theme contains smali changes
			resources.add("classes.dex");
		and.replaceResources(resources); //replace all modified files from compiled android file to original android file
		
		and.clear();
		
		write("finished patching theme. WARNING: theme may cause rom to be unstable. please verify that it actually works. If it doesn't work please report");

		return false;
	}
	
	private boolean preparedTranslations() {
		if (HelpFunctions.isOdex())
		{
			write("Rom must be Deodex to copy translations");
			Log.error("Copy translations failed because rom is odexed");
			return false;
		}
		AndroidFile destFile;
		File sourceDir;

		//		find all apks under source project
		write("searching translation apks");
		File f1=new File(params.TranslateProject);
		File[] source=f1.listFiles();
		Arrays.sort(source, new Comparator<File>() {
		    @Override
		    public int compare(File s1, File s2) {
		            return s1.compareTo(s2);
		    }
		});

		//		find all apks under destination project
		write("searching original apks");
		AndroidFile f2=new AndroidFile(params.activeProject);
		ArrayList<AndroidFile> dest=new ArrayList<AndroidFile>();
		HelpFunctions.getApkFiles(f2, dest,params.oTag);
		MyArrList temp=new MyArrList();
		dest=temp.sort(dest);

		//		remove any apk which does not exist on both places
		//		both arrays should be sorted
		int ind1=0;
		int ind2=0;
		while (ind1<source.length && ind2 <dest.size())
		{
			int comp=source[ind1].getName().compareToIgnoreCase(dest.get(ind2).getName());
			if (comp<0)
			{
				source[ind1]=null;
				ind1++;
			}
			if (comp>0)
			{
				dest.set(ind2,null);
				ind2++;
			}
			if (comp==0)
			{
				ind1++;
				ind2++;
			}
		}

		//		handle leftovers if one list is longer then the other
		while (ind1<source.length)
			source[ind1++]=null;
		while (ind2<dest.size())
			dest.set(ind2++,null);

		//		remove empty space from dest array
		while (dest.remove(null));
		//		remove empty space from source array
		int count=0;
		for (int i=0;i<source.length;i++)
		{
			if (source[i]!=null)
				count++;
		}
		File[] tempAr=new File[count];
		int j=0;
		for (int i=0;i<source.length;i++)
		{
			if (source[i]!=null) 
				tempAr[j++]=source[i];
		}
		source=tempAr;
		
		Log.debug("destenation apks length: "+dest.size()+" source apks length: "+source.length);
		//		when reached here, both arrays should have same size
		boolean errorFlag=false;
		List<String> failed=new ArrayList<String>();
		int numFiles=dest.size();
		for (int i=0;i<numFiles;i++)
		{
			destFile=dest.get(i);
			sourceDir=source[i];
			write("----------------"+sourceDir.getName()+" "+ i +"/"+numFiles+"--------------------");
			write("decompiling destination file "+destFile.getName());
			try {
			if (!destFile.decompile())
				throw new IOException("Decompile failed");

			//copy language files from source

				write("copying translations");	
				
				File destTemp=new File(destFile.getDecompiledPath(),"res");
				
				Log.debug("translation location: "+sourceDir);
				Log.debug("original location: "+destFile.getAbsolutePath());

				if (!HelpFunctions.copyDirectory(sourceDir, destTemp))
					throw new IOException("copy failed");
				write("compiling destenation file with patched language");
				if (destFile.compile()==null)
					throw new IOException("compile failed");
				write("moving resources.arsc from compiled to "+destFile.getName());

				ArrayList<String> resources=new ArrayList<String>();
				resources.add("resources.arsc");
				if (!destFile.replaceResources(resources))
					throw new IOException("patching resources failed");
				destFile.clear();
			} catch (IOException e) {
				errorFlag=true;
				Log.error("error while proccesing file: "+destFile.getAbsolutePath());
				write("Operation failed");
				destFile.clear();
				Log.writeEx(e);
				failed.add(sourceDir.getName());
				continue;
			}
		}
		write("finished translating");
		if (failed.size()!=0)
		{
			write("The following files failed");
			for (String st:failed)
				write(st);
		}
		return !errorFlag;
	}

	private boolean transltaions() {
		if (!params.isTransInit)
		{
			JOptionPane.showMessageDialog(null, "please initialize first!");
			return false;
		}
		if (HelpFunctions.isOdex())
		{
			write("Rom must be Deodex to copy translations");
			Log.error("Copy translations failed because rom is odexed");
			return false;
		}
		AndroidFile destFile,sourceFile;
		boolean trans_exists=false;

		//		find all apks under source project
		write("searching translation apks");
		AndroidFile f1=new AndroidFile(params.TranslateProject);
		ArrayList<AndroidFile> source=new ArrayList<AndroidFile>();
		HelpFunctions.getApkFiles(f1, source,params.tTag);
		MyArrList temp=new MyArrList();
		source=temp.sort(source);

		//		find all apks under destination project
		write("searching original apks");
		AndroidFile f2=new AndroidFile(params.activeProject);
		ArrayList<AndroidFile> dest=new ArrayList<AndroidFile>();
		HelpFunctions.getApkFiles(f2, dest,params.oTag);
		temp=new MyArrList();
		dest=temp.sort(dest);

		//		remove any apk which does not exist on both places
		//		both arrays should be sorted
		int ind1=0;
		int ind2=0;
		while (ind1<source.size() && ind2 <dest.size())
		{
			int comp=source.get(ind1).getName().compareToIgnoreCase(dest.get(ind2).getName());
			if (comp<0)
			{
				source.set(ind1,null);
				ind1++;
			}
			if (comp>0)
			{
				dest.set(ind2,null);
				ind2++;
			}
			if (comp==0)
			{
				ind1++;
				ind2++;
			}
		}

		//		handle leftovers if one list is longer then the other
		while (ind1<source.size())
			source.set(ind1++,null);
		while (ind2<dest.size())
			dest.set(ind2++,null);

		//		remove empty space from array
		while (dest.remove(null));
		while (source.remove(null));
		Log.debug("destenation apks length: "+dest.size()+" source apks length: "+source.size());
		//		when reached here, both arrays should have same size
		boolean errorFlag=false;
		for (int i=0;i<dest.size();i++)
		{
			destFile=dest.get(i);
			sourceFile=source.get(i);
			write("------------------------------------------------");
			write("decompiling destination file "+destFile.getName());
			if (!destFile.decompile())
			{
				Log.error("could not decompile "+destFile.getName());
				write("Error decompiling file. skiping to next");
				destFile.clear();
				errorFlag=true;
				continue;
			}

			write("decompiling source file");
			if (!sourceFile.decompile())
			{
				Log.error("could not decompile "+sourceFile.getName());
				write("Errod decompiling file. skiping to next");
				sourceFile.clear();
				errorFlag=true;
				continue;
			}

			//copy language files from source
			try {
				trans_exists=true;
				write("copying translations");	
				String layoutDir=""+File.separator+"res"+File.separator+"layout-";
				String sourceLayoutDir=layoutDir+params.transSuffix;
				String destLayoutDir=layoutDir+params.origSuffix;

				String langDir=""+File.separator+"res"+File.separator+"values-";
				
				String sourceLangDir=langDir+params.transSuffix;
				String destLangDir=langDir+params.origSuffix;
				
				File sourceTemp=new File(sourceFile.getDecompiledPath(),sourceLangDir);
				File destTemp=new File(destFile.getDecompiledPath(),destLangDir);
				
				if (!sourceTemp.exists()) //if suffix values-XX was not found - look for values-xx-rYY
				{
					trans_exists=false;
					if (!sourceTemp.getParentFile().exists()) //check if res folder even exists, if not then no translations available
					{
						write("no translations found");
						Log.debug("no translations were found for file: "+sourceFile.getAbsolutePath());
						continue; 
					}
					//					values-xx-rYY
					String name=sourceTemp.getName();
					for (File tempFile :sourceTemp.getParentFile().listFiles())
					{
						String tName=tempFile.getName();
						if (tName.startsWith(name))
						{
							tName=tName.replace(name,"");
							sourceTemp=new AndroidFile(sourceTemp.getAbsolutePath()+tName);
							destTemp=new AndroidFile(destTemp.getAbsolutePath()+tName);
							trans_exists=true;
							break;
						}
					}

					//					sourceTemp=new File(sourceTemp.getAbsolutePath()+"-rIL");
					//					destTemp=new File(destTemp.getAbsolutePath()+"-rIL");
				}
				if (!trans_exists)
				{
					write("no translations were found");
					Log.debug("no translations were found for file: "+sourceFile.getAbsolutePath());
					destFile.clear();
					sourceFile.clear();
					continue;
				}
				Log.debug("translation location: "+sourceTemp.getAbsolutePath());
				Log.debug("original location: "+destFile.getAbsolutePath());

				if (!HelpFunctions.copyDirectory(sourceTemp, destTemp))
				{
					Log.debug("failed to copy translations for file: "+destFile.getAbsolutePath());
					write("ERROR> failed to copy.");
					destFile.clear();
					sourceFile.clear();
					errorFlag=true;
					continue;
				}
				//copy layout folder if exists
				sourceTemp=new File(sourceFile.getDecompiledPath(),sourceLayoutDir);
				destTemp=new File(destFile.getDecompiledPath(),destLayoutDir);
				if (sourceTemp.exists())
				{
					if (!HelpFunctions.copyDirectory(sourceTemp, destTemp))
					{
						Log.debug("failed to copy layout for file: "+destFile.getAbsolutePath());
						write("ERROR> failed to copy.");
						destFile.clear();
						sourceFile.clear();
						errorFlag=true;
						continue;
					}
				}
			} catch (IOException e) {
				errorFlag=true;
				Log.debug("failed to copy translations for file: "+destFile.getAbsolutePath());
				write("ERROR> failed to copy.");
				destFile.clear();
				sourceFile.clear();
				Log.writeEx(e);
				continue;
			}

			write("compiling destenation file with patched language");
			if (destFile.compile()==null)
			{
				write("could not compile patched apk");
				destFile.clear();
				sourceFile.clear();
				errorFlag=true;
				continue;
			}
			write("moving resources.arsc from compiled to "+destFile.getName());

			ArrayList<String> resources=new ArrayList<String>();
			resources.add("resources.arsc");
			if (!destFile.replaceResources(resources))
			{
				write("failed. please see log");
				continue;
			}

			destFile.clear();
			sourceFile.clear();
//			sourceFile.delete();
		}
		write("finished translating");
		return !errorFlag;
	}

	

	private boolean music(String ruler)
	{
		String patchFile="MusicPlayer.apk";
		AndroidFile and=patchRuler(patchFile,ruler);
		if (and==null)
			return false;
		String file1=and.getDecompiledPath()+File.separator+"res"+File.separator+"layout"+File.separator+"list_item_thumnail_oneline.xml";
		String file2=and.getDecompiledPath()+File.separator+"res"+File.separator+"layout"+File.separator+"list_item_thumnail_twoline.xml";

		StringProcess.getInstance().changeQuotedFieldValueinLine(file1, ".*?<TextView.*", "marginRight", "26.0dip");
		StringProcess.getInstance().changeQuotedFieldValueinLine(file1, ".*?<TextView.*", "marginLeft", "12.0dip");
		StringProcess.getInstance().changeQuotedFieldValueinLine(file1, ".*?<ImageView.*", "marginLeft", "12.0dip");
		StringProcess.getInstance().changeQuotedFieldValueinLine(file1, ".*?<ImageView.*", "marginRight", "12.0dip");
		StringProcess.getInstance().changeQuotedFieldValueinLine(file1, ".*?<com.android.music.list.view.MpCheckBox.*", "marginLeft", "12.0dip");
		StringProcess.getInstance().changeQuotedFieldValueinLine(file1, ".*?<com.android.music.list.view.MpCheckBox.*", "marginRight", "12.0dip");
		StringProcess.getInstance().changeQuotedFieldValueinLine(file2, ".*?<TextView.*", "marginRight", "26.0dip");
		StringProcess.getInstance().changeQuotedFieldValueinLine(file2, ".*?<TextView.*", "marginLeft", "12.0dip");
		StringProcess.getInstance().changeQuotedFieldValueinLine(file2, ".*?<ImageView.*", "marginLeft", "12.0dip");
		StringProcess.getInstance().changeQuotedFieldValueinLine(file2, ".*?<ImageView.*", "marginRight", "12.0dip");
		StringProcess.getInstance().changeQuotedFieldValueinLine(file2, ".*?<com.android.music.list.view.MpCheckBox.*", "marginLeft", "12.0dip");
		StringProcess.getInstance().changeQuotedFieldValueinLine(file2, ".*?<com.android.music.list.view.MpCheckBox.*", "marginRight", "12.0dip");
		write("finished patching. compiling.");
		if (and.compile()==null)
		{
			write("failed to compile");
			return false;
		}
		write("copying changed files");
		ArrayList<String> resources=new ArrayList<String>();
		resources.add("res"+File.separator+"layout"+File.separator+"list_item_thumnail_twoline.xml"); //does this is the correct syntex to push the correct file?
		resources.add("res"+File.separator+"layout"+File.separator+"list_item_thumnail_oneline.xml");
		resources.add("resources.arsc");
		if (!and.replaceResources(resources))
		{
			write("failed to replace resources file");
			return false;
		}
		and.clear();
		write("File patched succsefully");
		return true;
	}

	private boolean contacts(String ruler)
	{
		String patchFile="Contacts.apk";
		AndroidFile and=patchRuler(patchFile,ruler);
		if (and==null)
			return false;
		write("finished patching. compiling. this could take a while..");
		if (and.compile()==null)
		{
			write("failed to compile");
			return false;
		}
		write("copying changed files");
		ArrayList<String> resources=new ArrayList<String>();
		resources.add("resources.arsc");
		if (!and.replaceResources(resources))
		{
			write("failed to replace resources file");
			return false;
		}
		and.clear();
		write("File patched succsefully");
		return true;
	}

	private boolean camera()
	{
		// TODO Auto-generated method stub
		return false;
	}

	private boolean sms() {
		String file="system"+File.separator+"app"+File.separator+"Mms.apk";
		AndroidFile and=new AndroidFile(new File(params.activeProject,file).getAbsolutePath());
		String patchLocation="res"+File.separator+"xml"+File.separator+"mms_config.xml";
		String patchFile=and.getDecompiledPath()+File.separator+patchLocation;
		if (!and.isFile())
		{
			Log.debug("File "+and.getAbsolutePath()+" could not be found.");
			return false;
		}

		write("Decompile file: "+and.getName());
		if (!and.decompile())
		{
			write("Could not decompile: "+and.getName());
			return false;
		}

		write("patching "+file);
		String linePattern=".*smsToMmsTextThreshold.*";
		String newVal="10";
		if (StringProcess.getInstance().changeTagValue(patchFile, linePattern, newVal)<0)
		{
			write("could not patch "+patchFile);
			Log.error("First patching of "+file+" failed");
			return false;
		}

		linePattern=".*name=\"recipientLimit\".*";
		newVal="100";
		if (StringProcess.getInstance().changeTagValue(patchFile, linePattern, newVal)<0)
		{
			write("could not patch "+patchFile);
			Log.error("second patching of "+file+" failed");
			return false;
		}

		String smaliFile=""+File.separator+"smali"+File.separator+"com"+File.separator+"android"+File.separator+"mms"+File.separator+"transaction"+File.separator+"SmsReceiverService.smali";
		String replace=".*invoke-static \\{\\}, Ljava/lang/System;->currentTimeMillis\\(\\)J.*";
		newVal=" invoke-virtual {p1}, Landroid/telephony/SmsMessage;->getTimestampMillis()J";
		if (StringProcess.getInstance().replaceFirst(and.getDecompiledPath()+smaliFile, replace, newVal)<0)
		{
			write("could not patch "+smaliFile);
			Log.error("third patching of "+file+" failed");
			return false;
		}
		newVal=" invoke-virtual {v0}, Landroid/telephony/gsm/CbMessage;->getTimestampMillis()J";
		if (StringProcess.getInstance().replaceFirst(and.getDecompiledPath()+smaliFile, replace, newVal)<0)
		{
			write("could not patch "+smaliFile);
			Log.error("third patching of "+file+" failed");
			return false;
		}

		write("finished patching.\ncompiling.");
		if (and.compile()==null)
		{
			write("failed to compile");
			return false;
		}
		write("copying changed files");
		ArrayList<String> resources=new ArrayList<String>();
		resources.add(patchLocation);
		resources.add("classes.dex");
		if (!and.replaceResources(resources))
		{
			write("failed to replace resources file");
			return false;
		}
		and.clear();
		write("File patched succsefully");
		return true;

	}

	private boolean phone() {
		String patchFile="system"+File.separator+"app"+File.separator+"Phone.apk";
		AndroidFile and=new AndroidFile(new File(params.activeProject,patchFile).getAbsolutePath());
		String patchLocation=and.getDecompiledPath()+File.separator+"smali"+File.separator+"com"+File.separator+"android"+File.separator+"phone"+File.separator+"Ringer$1.smali";
		
		if (!and.isFile())
		{
			Log.debug("File "+and.getAbsolutePath()+" could not be found.");
			return false;
		}
		write("Decompile file: "+and.getName());
		if (!and.decompile())
		{
			write("Could not decompile: "+and.getName());
			return false;
		}

		write("patching increasing ringer. file: "+patchLocation+".");
		String st=".*invoke-virtual [{]v1, v5, v4, v3[}], Landroid/media/AudioManager;->setStreamVolume(III)V.*";
		if (StringProcess.getInstance().delLine(patchLocation, st)<0)
		{
			write("could not patch "+patchLocation+"File might be patched already");
			Log.debug("patching of "+patchLocation+" failed");
			return false;
		}
		
		patchLocation=and.getDecompiledPath()+File.separator+"res"+File.separator+"layout"+File.separator+"call_card_person_info.xml";
		write("patching contact picture size. file: "+patchLocation+".");
		String line=".*<RelativeLayout.*";
		String fieldName="layout_width";
		String newVal="wrap_content";
		if (StringProcess.getInstance().changeQuotedFieldValueinLine(patchLocation,line, fieldName, newVal)<0)
		{
			write("could not patch "+patchLocation+"File might be patched already");
			Log.debug("patching of "+patchLocation+" failed");
			return false;
		}
		
		line=".*<RelativeLayout.*";
		fieldName="layout_height";
		newVal="185.0dip";
		if (StringProcess.getInstance().changeQuotedFieldValueinLine(patchLocation,line, fieldName, newVal)<0)
		{
			write("could not patch "+patchLocation+"File might be patched already");
			Log.debug("patching of "+patchLocation+" failed");
			return false;
		}
		
		line=".*photo.*";
		fieldName="layout_height";
		newVal="185.0dip";
		if (StringProcess.getInstance().changeQuotedFieldValueinLine(patchLocation,line, fieldName, newVal)<0)
		{
			write("could not patch "+patchLocation+"File might be patched already");
			Log.debug("patching of "+patchLocation+" failed");
			return false;
		}
		
		line=".*photo.*";
		fieldName="layout_width";
		newVal="185.0dip";
		if (StringProcess.getInstance().changeQuotedFieldValueinLine(patchLocation,line, fieldName, newVal)<0)
		{
			write("could not patch "+patchLocation+"File might be patched already");
			Log.debug("patching of "+patchLocation+" failed");
			return false;
		}
		
		
		write("finished patching. compiling back. this could take a while..");
		if (and.compile()==null)
		{
			write("failed to compile");
			return false;
		}
		write("finished compiling. replacing matching resource");
		ArrayList<String> resources=new ArrayList<String>();
		resources.add("resources.arsc");
		resources.add("res"+File.separator+"layout"+File.separator+"call_card_person_info.xml");
		resources.add("classes.dex");
		if (!and.replaceResources(resources))
		{
			write("failed to replace resources");
			return false;
		}
		and.clear();
		write("Patched succseffuly ");
		return true;

	}

	private boolean apn() {
		write("pathcing apn file");
		File dest=new File(params.activeProject,"system"+File.separator+"etc"+File.separator+"apns-conf.xml");
		if (!dest.isFile())
		{
			Log.debug("could not find apn file under: "+dest.getAbsolutePath());
			return false;
		}
		try {
			HelpFunctions.copy(new File(params.apn), dest);
			write("apn file copied succesfully");
			return true;
		} catch (Exception e) {
			Log.writeEx(e);
		}
		return true;

	}

	private boolean hosts() {
		write("pathcing hosts file");
		File dest=new File(params.activeProject,"system"+File.separator+"etc"+File.separator+"hosts");
		if (!dest.isFile())
		{
			Log.debug("could not find hosts file under: "+dest.getAbsolutePath());
			return false;
		}
		try {
			File source=new File(params.hosts);
			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dest,true),"UTF8"));
			BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(source),"utf-8"));

			write("adding lines to existing hosts file");
			write("this may cause duplicate records");
			String st=br.readLine();
			while (st!=null)
			{
				bw.write(st);
				bw.newLine();
				st=br.readLine();
			}
			bw.flush();
			bw.close();
			br.close();
			write("file patched succsessfully");
		} catch (UnsupportedEncodingException e) {
			Log.writeEx(e);
			return false;
		} catch (FileNotFoundException e) {
			Log.writeEx(e);
			return false;
		} catch (IOException e) {
			Log.writeEx(e);
			return false;
		}

		return true;

	}

	private boolean rtl()
	{
		Log.debug("Hebrew Patch Started");
		File dir=new File(params.activeProject,"system"+File.separator+"framework");
		write("This patch is done thnaks to Mena and classicaldude hebrew patch");
		if (JOptionPane.showConfirmDialog(null, "if rom is already fixed, don't try to fix it!\n continue?","Patch hebrew?",JOptionPane.YES_NO_OPTION)!=0)
			return false;
		JOptionPane.showMessageDialog(null, "Just press ok on any screen from here on.\nchoose to patch android 2.2 or 2.3\nChoose to patch files on this computer");
		write("place framework file");
		File sourceFile=new File(dir,"framework.jar");
		if (!sourceFile.exists())
		{
			JOptionPane.showMessageDialog(null, "could not find "+sourceFile.getAbsolutePath()+" file");
			return false;
		}
		File destDir=new File(params.hebFix_workDir,"framework");
		try {
			HelpFunctions.copy(sourceFile, new File(destDir,sourceFile.getName()));
		} catch (IOException e) {
			Log.writeEx(e);
		}
		//		sourceFile.renameTo(new File(destDir,sourceFile.getName()));

		write("place libandroid_runtime.so file");
		dir=new File(params.activeProject,"system"+File.separator+"lib");
		sourceFile=new File(dir,"libandroid_runtime.so");
		if (!sourceFile.exists())
		{
			JOptionPane.showMessageDialog(null, "could not find "+sourceFile.getAbsolutePath()+" file");
			return false;
		}
		destDir=new File(params.hebFix_workDir,"libs");
		try {
			HelpFunctions.copy(sourceFile, new File(destDir,sourceFile.getName()));
		} catch (IOException e) {
			Log.writeEx(e);
		}
		//		sourceFile.renameTo(new File(destDir,sourceFile.getName()));

		write("place libwebcore.so file");
		sourceFile=new File(dir,"libwebcore.so");
		if (!sourceFile.exists())
		{
			JOptionPane.showMessageDialog(null, "could not find "+sourceFile.getAbsolutePath()+" file");
			return false;
		}
		try {
			HelpFunctions.copy(sourceFile, new File(destDir,sourceFile.getName()));
		} catch (IOException e) {
			Log.writeEx(e);;
		}
		//			sourceFile.renameTo(new File(destDir,sourceFile.getName()));

		JOptionPane.showMessageDialog(null, "This patch uses Mena and classicaldude heb_fix application\n" +
				"please follow instruction on following screens. framework \nand *.so files have been copied for you.\n" +
				"Answer yes on all screens. chooses froyo (2.2) or GingerBread (2.3). eclair is not supported.\n" +
				"choose fix files on this computer.");
		HelpFunctions.run_command(new ArrayList<String>(Arrays.asList(params.hebFix)), true, false, params.hebFix_workDir);

		write("copying back patched files");
		write("copying framework file");
		dir=new File(params.hebFix_workDir,"generated");
		sourceFile=new File(dir,"framework.jar");
		if (!sourceFile.exists())
		{
			JOptionPane.showMessageDialog(null, "could not find "+sourceFile.getAbsolutePath()+" file");
			return false;
		}
		destDir=new File(params.activeProject,"system"+File.separator+"framework");
		try {
			HelpFunctions.copy(sourceFile, new File(destDir,sourceFile.getName()));
		} catch (IOException e) {
			Log.writeEx(e);
		}
		//		sourceFile.renameTo(new File(destDir,sourceFile.getName()));

		write("copying libandroid_runtime.so file");
		sourceFile=new File(dir,"libandroid_runtime.so");
		if (!sourceFile.exists())
		{
			JOptionPane.showMessageDialog(null, "could not find "+sourceFile.getAbsolutePath()+" file");
			return false;
		}
		destDir=new File(params.activeProject,"system"+File.separator+"lib");
		try {
			HelpFunctions.copy(sourceFile, new File(destDir,sourceFile.getName()));
		} catch (IOException e) {
			Log.writeEx(e);
		}
		//		sourceFile.renameTo(new File(destDir,sourceFile.getName()));

		write("copying libwebcore.so file");
		sourceFile=new File(dir,"libwebcore.so");
		if (!sourceFile.exists())
		{
			JOptionPane.showMessageDialog(null, "could not find "+sourceFile.getAbsolutePath()+" file");
			return false;
		}
		try {
			HelpFunctions.copy(sourceFile, new File(destDir,sourceFile.getName()));
		} catch (IOException e) {
			Log.writeEx(e);
		}
		write("Support in Hebrew patched succssefully\nAdding hebrew Locale");

		//		AndroidFile and=new AndroidFile(params.activeProject+File.separator+"system"+File.separator+"framework"+File.separator+"framework-res.apk");
		//		if (!and.exists())
		//		{
		//			write("could not find framework-res.apk");
		//			Log.error("could not find framework-res file");
		//			return false;
		//		}
		//		write("decompiling file "+and.getName());
		//		if (!and.decompile())
		//		{
		//			write("could not decompile "+and.getName());
		//			return false;
		//		}
		//		File temp=new File(and.getDecompiledPath(),"res"+File.separator+"values-iw-rIL");
		//		File temp2=new File(and.getDecompiledPath(),"res"+File.separator+"values-en-rGB");
		//		
		//		if (temp.exists())
		//		{
		//			write("hebrew locale already exists");
		//			Log.debug("hebrew locale already exists");
		//			and.clear();
		//			return true;
		//		}
		//		try {
		//			HelpFunctions.copyDirectory(temp2, temp);
		//		} catch (IOException e) {
		//			Log.writeEx(e);
		//		}
		//		write("compiling file "+and.getName());
		//		if (and.compile()==null)
		//		{
		//			write("could not compile file");
		//			return false;
		//		}
		//
		//		write("finished compiling.");
		//		write("copying to original and cleaning");
		//		String[] st={"resources.arsc"};
		//		and.replaceResources(st);
		//		and.clear();
		write("Finished succsefully");
		return true;
	}

	public void pushMessage(Message msg)
	{
		active=true;
		msgQueue.add(msg);
	}

	public void write(String st)
	{
		Message msg = new Message(MsgCommands.WRITE,st);
		sendMessage(msg);
	}

	public boolean deodexRom()
	{
		if (!params.isInitialized)
		{
			write("Please initialize first");
			return false;
		}
		boolean flag=deodexRom(params.activeProject,params.OrigOdexDir);
		flag = deodexRom(params.TranslateProject,params.TransOdexDir) && flag;

		write("Finished deodexing");
		if (!flag)
			return false;
		return flag;
	}

	private boolean deodexRom(String romFolder,String OdexFilesFolder)
	{
		write("building files list");
		Log.debug("Deodex: ");
		File dir=new File(romFolder);
		ArrayList<File> odex=new ArrayList<File>();
		HelpFunctions.getOdexFiles(dir,odex);
		if (odex.size()==0)
		{
			write("no odex file found. Rom is deodexed");
			Log.debug("no odex file was found");
			return true;
		}

		dir=new File(OdexFilesFolder);
		ArrayList<File> filesParams=new ArrayList<File>();
		HelpFunctions.getOdexFiles(dir,filesParams);
		String pm="";
		StringBuffer buf = new StringBuffer();
		for (File f1: filesParams)
		{
			buf.append(":"+f1.getName());
//			pm=pm+":"+f1.getName();
		}
		pm=buf.toString();
		Log.debug("params: "+pm);

		boolean flag=true;
		int numfiles=odex.size();
		int i=0;
		for (File f1: odex)
		{
			write("----------------"+f1.getName()+" "+ (i++) +"/"+numfiles+"--------------------");
			if (!deodexFile(f1,pm,OdexFilesFolder))
				flag=false;
		}
		if (!flag)
			return false;
		return flag;
	}

	public boolean deodexFile(File f1,String parameters, String OdexDir)
	{
		Log.debug("Deodexing file: "+f1.getAbsolutePath());
		String out="out";
		String ext="";
		String apk="apk";
		String jar="jar";
		String dirName=f1.getParent();

		//	detect extension
		if (dirName.endsWith("framework"))
			ext=jar;
		if (dirName.endsWith("app"))
			ext=apk;

		//	delete previous files
		File temp=new File(out);
		if (temp.exists())
			HelpFunctions.deleteDir(temp);
		temp=new File("classes.dex");
		temp.delete();

		if (!f1.exists())
		{
			Log.error(f1.getName()+" does not exist @ deodexed");
			return false;
		}
		//	run baksmali

		write("running baksmali");
		ArrayList<String> cmd=new ArrayList<String>(Arrays.asList(params.bakSmali));
		cmd.add("-x");
		cmd.add(f1.getAbsolutePath());
		cmd.add("-o");
		cmd.add(out);
		cmd.add("-d");
		cmd.add(OdexDir);
		boolean success=HelpFunctions.run_command(cmd);
		if (!success)
		{
			write("running baksmali second time");
			cmd=new ArrayList<String>(Arrays.asList(params.bakSmali));
			cmd.add("-c");
			cmd.add(parameters);
			cmd.add("-x");
			cmd.add(f1.getAbsolutePath());
			cmd.add("-o");
			cmd.add(out);
			cmd.add("-d");
			cmd.add(OdexDir);
			success=HelpFunctions.run_command(cmd);
			if (!success)
			{
				write("baksmali failed second time");
				Log.error("baksmali failed second time for file: "+f1.getAbsolutePath());
				return false;
			}
		}
		//	run smali
		write("running smali");
		cmd=new ArrayList<String>(Arrays.asList(params.smali));
		cmd.add("-o");
		cmd.add("classes.dex");
		cmd.add(out);
		success=HelpFunctions.run_command(cmd);
		if (!success)
		{
			Log.error("smali command failed");
			return false;
		}

		temp=new File(out);
		HelpFunctions.deleteDir(temp);

		//	add classes.dex to zip
		HelpFunctions.addToZip(f1.getAbsolutePath().replace(".odex", "."+ext), "classes.dex");

		//	clean up
		temp=new File("classes.dex");
		temp.delete();
		write("Deleting odex file");
		f1.delete();
		return true;
	}

	public boolean isActive()
	{
		return active;
	}
	public boolean root()
	{

		Log.debug("add root started");
		if (isRooted())
		{
			if (JOptionPane.showConfirmDialog(null, "Root has been found. continuing may break your rom. are you sure?\nSuggested: Choose No if base rom came with Zip file, and Yes if came with Odin","Confirmation",JOptionPane.YES_NO_OPTION)==1) //No was selected
			{
				//				No was selected. return with no error
				return true;
			}
			if (!delRoot())
			{
				Log.error("Could not remove existing root");
				return false;
			}
		}
		try {
			//		install Superuser application
			write("Copying SuperUser.apk");
			File source=new File("lib"+File.separator+"Superuser.apk");
			File dest=new File(params.activeProject+File.separator+"system"+File.separator+"app"+File.separator+"Superuser.apk");
			HelpFunctions.copy(source, dest);

			//		install su file
			//		set su in /system/xbin and create a symbolic link in /system/bin
			write("Copying su.apk");
			source=new File("lib"+File.separator+"su");
			dest=new File(params.activeProject+File.separator+"system"+File.separator+"xbin"+File.separator+"su");
			HelpFunctions.copy(source, dest);
			//		Add su to updater-script
			write("adding root to updater-script");
			File script=new File(params.activeProject+File.separator+"META-INF"+File.separator+"com"+File.separator+"google"+File.separator+"android"+File.separator+"updater-script");
			if (!script.isFile())
			{
				Log.debug("updater-script not found at: "+script.getAbsolutePath());
				write("kitchen does not support old update-script\nplease add su to update-script manually");
				return false;
			}
			//Removing previous occournces in update file of su patch
			if (StringProcess.getInstance().addAfterLine(script.getAbsolutePath(), ".*symlink.*", "symlink(\"/system/xbin/su\", \"/system/bin/su\");")<0)
			{
				Log.error("Could not patch update script");
				return false;
			}
			if (StringProcess.getInstance().addAfterLine(script.getAbsolutePath(),".*set_perm_recursive.*?\"/system/xbin.*","set_perm(0, 0, 04755, \"/system/xbin/su\");")<0)
			{
				Log.error("Could not patch update script");
				return false;
			}
			//		install busybox
			source=new File("lib"+File.separator+"busybox");
			dest=new File(params.activeProject+File.separator+"system"+File.separator+"xbin"+File.separator+"busybox");
			HelpFunctions.copy(source, dest);
		} catch (IOException e) {
			Log.writeEx(e);
			return false;
		}
		write("root added successfully");
		Log.debug("root added successfully");
		return true;
	}

	private boolean isRooted()
	{
		File su1=new File(params.activeProject+File.separator+"system"+File.separator+"bin"+File.separator+"su");
		File su2=new File(params.activeProject+File.separator+"system"+File.separator+"xbin"+File.separator+"su");
		File superuser=new File(params.activeProject+File.separator+"system"+File.separator+"app"+File.separator+"Superuser.apk");
		if (su1.isFile() || su2.isFile() || superuser.isFile())
			return true;
		return false;
	}

	private boolean delRoot()
	{
		File file=new File(params.activeProject+File.separator+"system"+File.separator+"bin"+File.separator+"su");
		if (file.exists() && !file.delete())
		{
			Log.error("Could not delete: "+file.getAbsolutePath());
			return false;
		}
		file=new File(params.activeProject+File.separator+"system"+File.separator+"xbin"+File.separator+"su");
		if (file.exists() && !file.delete())
		{
			Log.error("Could not delete: "+file.getAbsolutePath());
			return false;
		}
		file=new File(params.activeProject+File.separator+"system"+File.separator+"app"+File.separator+"Superuser.apk");
		if (file.exists() && !file.delete())
		{
			Log.error("Could not delete: "+file.getAbsolutePath());
			return false;
		}
		File script=new File(params.activeProject+File.separator+"META-INF"+File.separator+"com"+File.separator+"google"+File.separator+"android"+File.separator+"updater-script");
		if (!script.isFile())
		{
			Log.debug("updater-script not found at: "+script.getAbsolutePath());
			write("kitchen does not support old update-script\nplease add su to update-script manually");
			return false;
		}
		if (StringProcess.getInstance().delLine(script.getAbsolutePath(),".*bin/su\".*")<0)
		{
			return false;
		}
		return true;
	}

	public boolean addApplication(File and)
	{
		int ans=JOptionPane.showConfirmDialog(null, "Place "+and.getName()+" in data partition?\n If you choose no it will be placed in system partition.","App location",JOptionPane.YES_NO_OPTION);
		File dest=new File(params.activeProject);
		if (ans==0)
		{
			dest=new File(dest,"data"+File.separator+"app");
			if (!dest.exists())
				dest.mkdir();
		}
		else
		{
			dest=new File(dest,"system"+File.separator+"app"); //go into app folder
			if (!dest.exists())
				dest.mkdir();
		}
		dest=new File(dest,and.getName());
		ArrayList<String> command=new ArrayList<String>(Arrays.asList(params.zip));
		command.add("e");
		command.add(and.getAbsolutePath());
		command.add("lib"+File.separator+"armeabi");
		command.add("-o\""+params.activeProject+File.separator+"system"+File.separator+"lib\"");
		command.add("-y");
		HelpFunctions.run_command(command);
		write("copying file");
		try {
			HelpFunctions.copy(and, dest);
		} catch (IOException e) {
			Log.writeEx(e);
			return false;
		}
		write("application was added successfully");
		return true;
	}

	public boolean clearTempFiles()
	{
		Log.debug("clearing temp files: ");
		write("Deleteing temporary files...");
		delTemps(new File(params.activeProject));
		write("Deleting odex files..");
		HelpFunctions.deleteDir(new File(params.OrigOdexDir));
		HelpFunctions.deleteDir(new File(params.TransOdexDir));
		HelpFunctions.deleteDir(new File(params.tempDir));
		write("Finished");
		return true;
	}
	/**
	 * Deletes every file which ends in params.decompiledTag or params.compiledTag
	 * in current folder and sub-folders recursively.
	 * @param file - folder to begin at
	 */
	public void delTemps(File file)
	{
		if (file.isDirectory() && !file.getName().toLowerCase().endsWith(params.decompiledTag))
		{
			File[] temp=file.listFiles();
			for (File b:temp)
				delTemps(b);
		}
		else
		{
			if (file.getName().toLowerCase().endsWith(params.decompiledTag))
			{
				write("deleting "+file.getName());
				Log.debug("deleting "+file.getAbsolutePath());
				HelpFunctions.deleteDir(file);
			}
			if (file.getName().toLowerCase().endsWith(params.compiledTag))
			{
				write("deleting "+file.getName());
				Log.debug("deleting "+file.getAbsolutePath());
				file.delete();
			}
		}
	}
	public boolean zipAlign()
	{
		if (!params.isInitialized)
		{
			JOptionPane.showMessageDialog(null, "Please initialize first!");
			return false;
		}
		AndroidFile AndroidFile2=null;
		String fname;
		File dir=new File(params.activeProject);
		ArrayList<AndroidFile> list=new ArrayList<AndroidFile>();
		HelpFunctions.getApkFiles(new AndroidFile(dir.getAbsolutePath()), list);
		int max=list.size();
		int i=0;
		for (AndroidFile AndroidFile:list)
		{
			fname=AndroidFile.getAbsolutePath();
			write("Zipaligning AndroidFile ("+(i++)+"/"+max+"): "+fname);
			ArrayList<String> command=new ArrayList<String>(Arrays.asList(params.zipAlign));
			command.add(fname);
			command.add(fname+params.decompiledTag);
			if (HelpFunctions.run_command(command))
			{
				AndroidFile.delete();
				AndroidFile2=new AndroidFile(AndroidFile.getDecompiledPath());
				AndroidFile2.renameTo(AndroidFile);
			}
			else
			{
				Log.error("Zipalign failed for: "+AndroidFile.getAbsolutePath());
				write("-----------------------------------------------");
				write("Zipalign failed");
				write("-----------------------------------------------");
			}

		}
		return true;
	}
	public AndroidFile patchRuler(String patchFile,String ruler)
	{
		String[] temp=ruler.split(";");
		String valuesSuffix="";
		String newVal="";
		if (temp.length==2)
		{
			valuesSuffix=temp[0];
			newVal=temp[1];
		}
		else
			Log.error("invalid ruler string recieved " + ruler);
		Log.debug("patchRuler: "+patchFile);
		File found=HelpFunctions.finapkdFile(params.activeProject,patchFile);
		if (found==null)
		{
			Log.error(patchFile+" could not be found");
			return null;
		}

		AndroidFile and=new AndroidFile(found.getAbsolutePath());
		write("Decompile file: "+and.getName());
		if (!and.decompile())
		{
			Log.debug("Could not decompile: "+and.getAbsolutePath());
			return null;
		}

		if (valuesSuffix.compareTo("")!=0) //if string is not empty , use as suffix
			valuesSuffix="-"+valuesSuffix;
		String patchLocation=""+File.separator+"res"+File.separator+"values"+valuesSuffix+File.separator+"strings.xml";

		String linePattern=".*twstr_indexlist.*";
		write("Patching "+patchFile);
		Log.debug("Patch Location="+patchLocation+" newVal="+newVal);
		if (StringProcess.getInstance().changeTagValue(and.getDecompiledPath()+patchLocation, linePattern, newVal)<0)
		{
			write("Could not patch "+patchFile);
			Log.error("First patching of "+patchFile+" failed");
			return null;
		}

		linePattern=".*name=\"indexlist\".*";
		if (StringProcess.getInstance().changeTagValue(and.getDecompiledPath()+patchLocation, linePattern, newVal)<0)
		{
			Log.error("Second patching of "+patchFile+" failed");
			return null;
		}
		return and;
	}

	public boolean compile(String workFile)
	{
		if (workFile==null)
		{
			JOptionPane.showMessageDialog(null, "please choose active project");
			return true;
		}
		AndroidFile and=new AndroidFile(workFile);
		if (!and.isDecompiled())
		{
			write("file chosen is not decompiled");
			return false;
		}
		write("compiling: "+and.getName());
		String ret=and.compile();
		if (ret!=null)
		{
			write("finished compiling");
			return true;
		}
		else
			write("Compiling of "+and.getAbsolutePath()+" Failed ");
		return false;
	}

	public boolean decompile(String workFile)
	{
		if (workFile==null)
		{
			JOptionPane.showMessageDialog(null, "please choose active project");
			return true;
		}
		AndroidFile and=new AndroidFile(workFile);
		if (!and.isFile())
		{
			write("file selected is not an apk or jar file");
			return false;
		}

		write("decompiling file: "+and.getName());
		if (and.decompile())
		{
			write("finished decompiling");
			return true;
		}
		else
		{
			write("decompile failed");
			return false;
		}
	}

	public boolean initProject()
	{
		ArrayList<AndroidFile> searchResaults;
		ArrayList<String> cmd;
		//register all apks under translation framework folder
		searchResaults=new ArrayList<AndroidFile>();
		AndroidFile translatedFrameworkDir=new AndroidFile(params.TranslateProject+File.separator+"system"+File.separator+"framework");
		if (!translatedFrameworkDir.exists())
		{
			Log.debug("could not find framework folder under: "+translatedFrameworkDir.getAbsolutePath());
		}
		else
		{
			HelpFunctions.getApkFiles(translatedFrameworkDir,searchResaults);
			for (AndroidFile file : searchResaults)
			{
				write("adding "+file.getName()+" to apktool path");
				cmd=new ArrayList<String>(Arrays.asList(params.apktool));
				cmd.add("if");
				cmd.add(file.getAbsolutePath());
				cmd.add(params.tTag);
				HelpFunctions.run_command(cmd, false, false, params.libDir);
			}
			params.isTransInit=true;
		}

		searchResaults=new ArrayList<AndroidFile>();
		String location=params.activeProject+"/system/framework";
		AndroidFile file1=new AndroidFile(location);
		if (!file1.isDirectory())
		{
			Log.debug("could not find framework folder under: "+file1.getAbsolutePath());
			return false;
		}

		//register all apks under framework folder
		HelpFunctions.getApkFiles(file1,searchResaults);
		
		for (AndroidFile file : searchResaults)
		{
			write("adding "+file.getName()+" to apktool path");
			cmd=new ArrayList<String>(Arrays.asList(params.apktool));
			cmd.add("if");
			cmd.add(file.getAbsolutePath());
			cmd.add(params.oTag);
			HelpFunctions.run_command(cmd, false, false, params.libDir);
		}
		//		copy odex Files from framework folder
		write("all apks registered");
		copyOdexFiles();
		params.isInitialized=true;
		return true;
	}
	
	private void copyOdexFiles()
	{
		//copy original odex files
		File dir=new File(params.activeProject,"system"+File.separator+"framework");
		
		for (File f1: dir.listFiles(new jarFileFilter()))
		{
			File f2=new File(f1.getAbsolutePath().replace(".jar",".odex"));
			if (f2.exists())
			{
				try {
					write("copying "+f2.getName());
					HelpFunctions.copy(f2,new File(params.OrigOdexDir,f2.getName()));
				} catch (IOException e)
				{
					write("copy failed");
					Log.writeEx(e);
				}
			}
			else
			{
				try {
					write("copying "+f1.getName());
					HelpFunctions.copy(f1,new File(params.OrigOdexDir,f2.getName()));
				} catch (IOException e)
				{
					write("copy failed");
					Log.writeEx(e);
				}
			}
		}

		//copy translation odex files
		dir=new File(params.TranslateProject,"system"+File.separator+"framework");
		File[] jarFiles=dir.listFiles(new jarFileFilter());
		if (jarFiles==null) //no odex file found
			return; 
		for (File f1: jarFiles)
		{
			File f2=new File(f1.getAbsolutePath().replace(".jar",".odex"));
			if (f2.exists())
			{
				try {
					write("copying "+f2.getName());
					HelpFunctions.copy(f2,new File(params.TransOdexDir,f2.getName()));
				} catch (IOException e)
				{
					write("copy failed");
					Log.writeEx(e);
				}
			}
			else
			{
				try {
					write("copying "+f1.getName());
					HelpFunctions.copy(f1,new File(params.TransOdexDir,f2.getName()));
				} catch (IOException e)
				{
					write("copy failed");
					Log.writeEx(e);
				}
			}
		}
	}

	public boolean extractRom(String romFile)
	{
		File tempDir=new File(params.tempDir);
		String workFolder=params.activeProject;
		buildWorkDir(workFolder);
		Log.debug("extract rom: "+romFile+" to folder: "+workFolder);
		File file=new File(romFile);
		
		if (file.isDirectory())
		{
			Log.debug("A directory selected. this is not supported format");
			return false;
		}
		if (!file.isFile())
		{
			Log.error("file is invalid");
			return false;
		}
		//unmount rfs files as they prevent deleting temp folder
		File factory=new File(tempDir,"factoryfs.rfs");
		String[] cmds={"sudo","umount"};
		if (factory.exists() && System.getProperty("os.name").contains("Linux"))
		{
				ArrayList<String> cmd=new ArrayList<String>(Arrays.asList(cmds));
				cmd.add(factory.getAbsolutePath());
				HelpFunctions.run_command(cmd);
		}
		File cache=new File(tempDir,"cache.rfs");
		if (cache.exists() && System.getProperty("os.name").contains("Linux"))
		{
			ArrayList<String> cmd=new ArrayList<String>(Arrays.asList(cmds));
			cmd.add(cache.getAbsolutePath());
			HelpFunctions.run_command(cmd);
		}
		HelpFunctions.deleteDir(tempDir);
		tempDir.mkdirs();
		if (file.exists() && file.getAbsolutePath().toLowerCase().endsWith(".rar")) {
			if (!extractRar(file,tempDir))
				return false;
		}
		else if (file.exists() && (file.getAbsolutePath().toLowerCase().endsWith("tar") || file.getAbsolutePath().toLowerCase().endsWith(".tar.md5"))) {
			if (!extractTar(file,tempDir))
				return false;
		}
		else if (file.exists() && file.getName().toLowerCase().endsWith("rfs") && file.getName().startsWith("factory")) {
			return extractRfs(file,tempDir,workFolder);
		}
			
		else if (file.exists() && file.getAbsolutePath().toLowerCase().endsWith("zip")) {
			write("extracting "+file.getAbsolutePath()+". this could take a moment");
			return HelpFunctions.extractZipToDest(romFile,workFolder);
		}
		else {
			Log.debug("File is in unrecognized rom format");
			Log.debug("please extract manually");
			return false;
		}
		if (!extractTempFolder(tempDir,workFolder))
			return false;
		write("Extract completed successfully");
		initProject();
		return true;

	}
	
	private boolean extractRar(File rarFile, File tempDir)
	{
		boolean ret=false;
		write("extracting "+rarFile.getAbsolutePath()+". this could take a while.");
		String romFile=rarFile.getAbsolutePath();
		if (isEncrypted(romFile))
		{
			String pass=JOptionPane.showInputDialog("archive is password protected. Please enter password.");
			ret=HelpFunctions.extractZipToDest(romFile,tempDir.getAbsolutePath(),pass);
		}
		else
		{
			ret=HelpFunctions.extractZipToDest(romFile,tempDir.getAbsolutePath());
		}
		if (!ret)
			return false;
		
		for (File f : tempDir.listFiles())
		{					
			if (f.getName().toLowerCase().endsWith("tar") ||f.getName().toLowerCase().endsWith("tar.md5"))
			{
				if (!(extractTar(f,tempDir)))
					return false;
			}
		}
		return true;
	}
	
	private boolean extractTar(File tarFile,File tempDir)
	{
		write("extracting "+tarFile.getAbsolutePath()+". this could take a moment");
		return HelpFunctions.extractTarToDest(tarFile.getAbsolutePath(),tempDir.getAbsolutePath());
	}
	
	private boolean extractTempFolder(File tempDir, String workFolder)
	{
		if (!extractRfs(new File(tempDir,"factoryfs.rfs"), tempDir, workFolder))
			return false;
		if (!extractRfs(new File(tempDir,"cache.rfs"), tempDir, workFolder))
			return false;
		File modem=new File(tempDir,"modem.bin");
		if (modem.exists())
		{
			write("modem found. moving it to rom");
			modem.renameTo(new File(workFolder+File.separator+"updates"+File.separator+"modem.bin"));
		}
		File kernel=new File(tempDir,"zImage");
		if (kernel.exists())
		{
			write("kernel found. moving it to rom");
			kernel.renameTo(new File(workFolder+File.separator+"updates"+File.separator+"zImage"));
		}
		return true;
	}
	
	private boolean extractRfs(File rfsFile, File tempDir, String workFolder)
	{
		//speacial treament for cache.rfs
		if (rfsFile.getName().equalsIgnoreCase("cache.rfs"))
		{
			File cache=new File(tempDir,"cache");
			cache.mkdirs();
			if (System.getProperty("os.name").contains("Linux"))
			{
				write("extracting cache.rfs");
				ArrayList<String> cmd2=new ArrayList<String>();
				cmd2.add("sudo");
				cmd2.add("mount");
				cmd2.add("-o");
				cmd2.add("loop");
				cmd2.add(tempDir.getAbsolutePath()+File.separator+"cache.rfs");
				cmd2.add(cache.getAbsolutePath());
				if (!HelpFunctions.run_command(cmd2,true))
					return false;
			}
			else JOptionPane.showMessageDialog(null, "Please manually extract "+tempDir.getAbsolutePath()+File.separator+"cache.rfs to "+tempDir.getAbsolutePath()+". before pressing OK");
			cache = new File(cache.getAbsolutePath()+File.separator+"recovery"+File.separator+"sec_csc.zip");
			if (cache.exists())
			{
				write("extracting cache - sec_csc.zip.");
				if (!HelpFunctions.extractZipfiles(cache.getAbsolutePath(),workFolder,"system"))
					return false;
			}
			return true;
		}
		
		if (System.getProperty("os.name").contains("Linux"))
		{
			write("extracting "+rfsFile.getAbsolutePath());
			ArrayList<String> cmd=new ArrayList<String>();
			cmd.add("sudo");
			cmd.add("mount");
			cmd.add("-o");
			cmd.add("loop");
			cmd.add(rfsFile.getAbsolutePath());
			cmd.add(workFolder+File.separator+"system");
			if (!HelpFunctions.run_command(cmd,true))
				return false;
		}
		else
			JOptionPane.showMessageDialog(null, "Please manually extract "+tempDir.getAbsolutePath()+File.separator+"factory.rfs to "+workFolder+File.separator+"system. before pressing OK");
		if (rfsFile.getName().equalsIgnoreCase("factoryfs.rfs"))
		{
			write("removing symbolic links from "+workFolder);
			return deleteSymbolicLinks(workFolder);
		}
		return true;
	}
	
	private boolean isEncrypted(String romFile)
	{
		ArrayList<String> cmd=new ArrayList<String>();
		cmd.add("7z");
		cmd.add("L");
		cmd.add("-SLT");
		cmd.add(romFile);
		cmd.add("|");
		if (System.getProperty("os.name").contains("Linux"))
		{
			cmd.add("grep");
			cmd.add("\"Encrypted = +\"");
			cmd.add(">");
			cmd.add("/dev/null");
		}
		else
		{
			cmd.add("FINDSTR");
			cmd.add("/C:\"Encrypted = +\"");
			cmd.add(">");
			cmd.add("NUL");
		}
		
		return HelpFunctions.run_command(cmd);
	}
	
	private void buildWorkDir(String location)
	{
		File loc=new File(location);
		clearWorkDir(location);
		File f=new File(loc,"system");
		f.mkdirs();
		f=new File(loc,"data");
		f.mkdirs();
		f=new File(loc,"updates");
		f.mkdirs();
		f=new File(loc,"sdcard");
		f.mkdirs();
		f=new File("lib"+File.separator+"rom.zip");
		HelpFunctions.extractZipToDest(f.getAbsolutePath(),loc.getAbsolutePath());
	}

	private void clearWorkDir(String location)
	{
		File f=new File(location);
		HelpFunctions.deleteDir(f);
		f.mkdir();
	}
	public boolean deleteSymbolicLinks(String location)

	{
		boolean err=false;
		String path=location;
		Log.debug("Delteing symbolic links from: "+path);
//		remove symbolic links from list of known symbolic links
		File temp;
		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(params.symlinkslist),"utf-8"));
			String line=br.readLine();
			while (line!=null)
			{
				temp=new File(path+File.separator+line);
//				System.out.println(temp.length());
				if (!temp.delete() && temp.exists())
				{
					Log.debug("Could not delete symbolic link file: "+line);
					err=true;
				}
				line=br.readLine();
			}
			br.close();
		} catch (UnsupportedEncodingException e) {
			Log.writeEx(e);
		} catch (FileNotFoundException e) {
			Log.writeEx(e);
		} catch (IOException e) {
			Log.writeEx(e);
		}
		//remove symbolic links from xbin folder
		temp=new File(location,"system"+File.separator+"xbin");
		if (!temp.isDirectory())
		{
			Log.error("Failed to locate folder: "+temp.getAbsolutePath());
			return false;
		}
		for (File a: temp.listFiles())
		{
			if (a.length()<60) //file shorter then 60 bytes is believed to be a symbolic link. smallest Opearating System file found was 191 byte
				a.delete();
		}
		//		remove symbolic links from bin folder
		temp=new File(location,"system"+File.separator+"bin");
		if (!temp.isDirectory())
		{
			Log.error("Failed to locate folder: "+temp.getAbsolutePath());
			return false;
		}
		for (File a: temp.listFiles())
		{
			if (a.length()<60) //file shorter then 60 bytes is believed to be a symbolic link. smallest Opearating System file found was 191 byte
				a.delete();
		}
		
		return !err;
	}
	
	private boolean test()
	{
		return true;
	}
}
